#include "quickjspp.hpp"
#include <iostream>

class MyClass {
public:
  MyClass() {}
  MyClass(std::vector<int>) {}

  double member_variable = 5.5;
  std::string member_function(const std::string &s) { return "Hello, " + s; }
};

void println(qjs::rest<std::string> args) {
  for (auto const &arg : args)
    std::cout << arg << " ";
  std::cout << "\n";
}

int main() {
  qjs::Runtime runtime;
  qjs::Context context(runtime);
  try {
    // export classes as a module
    auto &module = context.addModule("MyModule");
    module.function<&println>("println");
    module.class_<MyClass>("MyClass")
        .constructor<>()
        .constructor<std::vector<int>>("MyClassA")
        .fun<&MyClass::member_variable>("member_variable")
        .fun<&MyClass::member_function>("member_function");
    // import module
    context.eval(R"xxx(
            import * as my from 'MyModule';
            globalThis.my = my;
        )xxx",
                 "<import>", JS_EVAL_TYPE_MODULE);
    // evaluate js code
    context.eval(R"xxx(
            let v1 = new my.MyClass();
            v1.member_variable = 1;
            let v2 = new my.MyClassA([1,2,3]);
            function my_callback(str) {
              my.println("at callback:", v2.member_function(str));
            }
        )xxx");

    // callback
    auto cb =
        (std::function<void(const std::string &)>)context.eval("my_callback");
    cb("world");
  } catch (qjs::exception) {
    auto exc = context.getException();
    std::cerr << (std::string)exc << std::endl;
    if ((bool)exc["stack"])
      std::cerr << (std::string)exc["stack"] << std::endl;
    return 1;
  }

  try {
    context.evalFile("../example.js");
    auto cb = (std::function<std::string(const std::string &)>)context.eval(
        "StringToHex");

    std::string a =
        "0103ff0001000100010001000100010001000100010001000100010001000100010001"
        "0001000100010001000100010001000100010001000100010001000100010001000100"
        "0100010001000100010001000100010001000100010001000100010001000100010001"
        "0001000100010001000100010001000100010001000100010001000100010001000100"
        "0100010001000100010001000100010001000100010001000100010001000100010001"
        "0001000100010001000100010001000100010001000100010001000100010001000100"
        "0100010001000100010001000100010001000100010001000100010001000100010001"
        "0001000100010001000100011122";

    std::string retValue = cb(a);
    std::cout << "StringToHex: " << retValue << "\n";
  } catch (qjs::exception) {
    auto exc = context.getException();
    std::cerr << (std::string)exc << std::endl;
    if ((bool)exc["stack"])
      std::cerr << (std::string)exc["stack"] << std::endl;
    return 1;
  }
  
  return 0;
}